
namespace AdminSG3L.Domain.Entities;

public class AppUser : EntityBase
{
    /// <summary>    /// <summary>
    /// 创建 AppUser 对象的工厂方法。
    /// </summary>
    /// <param name="nickName">昵称</param>
    /// <param name="username">用户名</param>
    /// <param name="plainPassword">明文密码</param>
    /// <param name="email">邮箱</param>
    /// <param name="avatar">头像</param>
    /// <param name="description">用户描述</param>
    /// <param name="isSystem">是否为系统用户</param>
    /// <returns>AppUser 对象</returns>
    public static AppUser Create(string? nickName, string username, string plainPassword, string email, string? avatar, string? description, bool isSystem = false)
    {
        var salt = GenerateSalt();
        var hash = HashPassword(plainPassword, salt);
        var user = new AppUser(nickName, username, hash, email, avatar, salt, description);
        user.IsSystem = isSystem;
        return user;
    }

    /// <summary>
    /// 用户昵称，可选。
    /// </summary>
    public string? NickName { get; private set; }
    /// <summary>
    /// 用户名，唯一标识。
    /// </summary>
    public string Username { get; private set; }
    /// <summary>
    /// 哈希后的用户密码。
    /// </summary>
    public string PasswordHash { get; private set; }
    /// <summary>
    /// 用户邮箱。
    /// </summary>
    public string Email { get; private set; }
    /// <summary>
    /// 用户头像，可选。
    /// </summary>
    public string? Avatar { get; private set; }
    /// <summary>
    /// 密码盐。
    /// </summary>
    public string PasswordSalt { get; private set; }
    /// <summary>
    /// 用户拥有的角色集合。
    /// </summary>
    public ICollection<AppRole> Roles { get; private set; } = [];

    /// <summary>
    /// RefreshToken，用于双token机制
    /// </summary>
    public string? RefreshToken { get; set; }
    /// <summary>
    /// RefreshToken过期时间
    /// </summary>
    public DateTime? RefreshTokenExpireAt { get; set; }

    /// <summary>
    /// 登录次数
    /// </summary>
    public int LoginCount { get; private set; } = 0;

    /// <summary>
    /// 最后登录时间
    /// </summary>
    public DateTime? LastLoginAt { get; private set; }

    /// <summary>
    /// 最后登录IP
    /// </summary>
    public string? LastLoginIp { get; private set; }

    /// <summary>
    /// 设置RefreshToken及过期时间
    /// </summary>
    public void SetRefreshToken(string? refreshToken, DateTime expireAt)
    {
        RefreshToken = refreshToken;
        RefreshTokenExpireAt = expireAt;
    }

    /// <summary>
    /// 清除RefreshToken
    /// </summary>
    public void ClearRefreshToken()
    {
        RefreshToken = null;
        RefreshTokenExpireAt = null;
    }

    /// <summary>
    /// 私有构造函数，仅限工厂方法调用。
    /// </summary>
    /// <param name="nickName">昵称</param>
    /// <param name="username">用户名</param>
    /// <param name="passwordHash">哈希密码</param>
    /// <param name="email">邮箱</param>
    /// <param name="avatar">头像</param>
    /// <param name="passwordSalt">密码盐</param>
    /// <param name="description">用户描述</param>
    private AppUser(string? nickName, string username, string passwordHash, string email, string? avatar, string passwordSalt, string? description)
    {
        NickName = nickName;
        Username = username;
        PasswordHash = passwordHash;
        Email = email;
        Avatar = avatar;
        PasswordSalt = passwordSalt;
        Description = description; // 赋值给基类属性
    }

    /// <summary>
    /// 创建 AppUser 对象的工厂方法，自动生成密码盐并加密密码。
    /// </summary>
    /// <param name="nickName">昵称</param>
    /// <param name="username">用户名</param>
    /// <param name="plainPassword">明文密码</param>
    /// <param name="email">邮箱</param>
    /// <param name="avatar">头像</param>
    /// <param name="description">用户描述</param>
    /// <returns>AppUser 对象</returns>
    public static AppUser Create(string? nickName, string username, string plainPassword, string email, string? avatar, string? description)
    {
        var salt = GenerateSalt();
        var hash = HashPassword(plainPassword, salt);
        return new AppUser(nickName, username, hash, email, avatar, salt, description);
    }

    /// <summary>
    /// 生成一个随机的密码盐，返回 Base64 字符串。
    /// </summary>
    /// <returns>Base64 编码的盐字符串</returns>
    private static string GenerateSalt()
    {
        var bytes = new byte[16];
        using (var rng = System.Security.Cryptography.RandomNumberGenerator.Create())
        {
            rng.GetBytes(bytes);
        }
        return Convert.ToBase64String(bytes);
    }

    /// <summary>
    /// 使用 PBKDF2 算法对密码和盐进行加密，返回哈希后的 Base64 字符串。
    /// </summary>
    /// <param name="password">明文密码</param>
    /// <param name="salt">Base64 编码的盐</param>
    /// <returns>Base64 编码的哈希密码</returns>
    private static string HashPassword(string password, string salt)
    {
        var saltBytes = Convert.FromBase64String(salt);
        using var pbkdf2 = new System.Security.Cryptography.Rfc2898DeriveBytes(password, saltBytes, 10000, System.Security.Cryptography.HashAlgorithmName.SHA256);
        var hash = pbkdf2.GetBytes(32); // 256位
        return Convert.ToBase64String(hash);
    }

    /// <summary>
    /// 给用户赋予角色。
    /// </summary>
    /// <param name="role">要添加的角色</param>
    public void AddRole(AppRole role)
    {
        if (!Roles.Contains(role))
        {
            Roles.Add(role);
        }
    }

    /// <summary>
    /// 移除用户角色。
    /// </summary>
    /// <param name="role">要移除的角色</param>
    public void RemoveRole(AppRole role)
    {
        Roles.Remove(role);
    }

    /// <summary>
    /// 验证输入的明文密码是否与当前用户密码一致。
    /// </summary>
    /// <param name="plainPassword">明文密码</param>
    /// <returns>是否一致</returns>
    public bool VerifyPassword(string plainPassword)
    {
        var hash = HashPassword(plainPassword, PasswordSalt);
        return hash == PasswordHash;
    }

    /// <summary>
    /// 更新用户密码和密码盐。
    /// </summary>
    /// <param name="newPassword">新明文密码</param>
    public void UpdatePassword(string newPassword)
    {
        if (IsSystem)
        {
            throw new InvalidOperationException("系统用户密码不允许通过此方法修改");
        }
        var newSalt = GenerateSalt();
        var newHash = HashPassword(newPassword, newSalt);
        PasswordSalt = newSalt;
        PasswordHash = newHash;
    }

    public void Update(string? nickName, string email, string? avatar, string? description, bool? isActive = null)
    {
        if (IsSystem)
        {
            throw new InvalidOperationException("系统用户不允许修改");
        }
        NickName = nickName;
        Email = email;
        Avatar = avatar;
        Description = description;
        if (isActive.HasValue)
        {
            IsActive = isActive.Value;
        }
    }

    /// <summary>
    /// 更新登录信息
    /// </summary>
    /// <param name="loginIp">登录IP</param>
    public void UpdateLoginInfo(string? loginIp = null)
    {
        LoginCount++;
        LastLoginAt = DateTime.UtcNow;
        LastLoginIp = loginIp;
    }

    /// <summary>
    /// 更新用户资料
    /// </summary>
    /// <param name="nickName">昵称</param>
    /// <param name="email">邮箱</param>
    /// <param name="description">个人简介</param>
    public void UpdateProfile(string? nickName, string? email, string? description)
    {
        if (IsSystem)
        {
            throw new InvalidOperationException("系统用户不允许修改");
        }
        NickName = nickName;
        if (!string.IsNullOrEmpty(email))
        {
            Email = email;
        }
        Description = description;
    }

    /// <summary>
    /// 更新用户头像
    /// </summary>
    /// <param name="avatarUrl">头像URL</param>
    public void UpdateAvatar(string? avatarUrl)
    {
        if (IsSystem)
        {
            throw new InvalidOperationException("系统用户不允许修改");
        }
        Avatar = avatarUrl;
    }
}